package com.kxmall.common.helper;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.kxmall.common.constant.UserConstants;
import com.kxmall.common.exception.UtilException;
import com.kxmall.common.core.domain.model.LoginUser;
import com.kxmall.common.enums.DeviceType;
import com.kxmall.common.enums.UserType;
import com.kxmall.common.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 登录鉴权助手
 *
 * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
 * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
 * 可以组成 用户类型与设备类型多对多的 权限灵活控制
 *
 * 多用户体系 针对 多种用户类型 但权限控制不一致
 * 可以组成 多用户类型表与多设备类型 分别控制权限
 *
 * @author kxmall
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {

    public static final String JOIN_CODE = ":";
    public static final String LOGIN_USER_KEY = "loginUser";

    /**
     * 登录系统
     *
     * @param loginUser 登录用户信息
     */
    public static void login(LoginUser loginUser) {
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
        StpUtil.login(loginUser.getLoginId());
        setLoginUser(loginUser);
    }

    /**
     * 登录系统 基于 设备类型（增强版）
     * 针对相同用户体系不同设备，确保Token隔离
     *
     * @param loginUser 登录用户信息
     * @param deviceType 设备类型
     */
    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
        try {
            // 1. 清理可能的ThreadLocal残留
            SaHolder.getStorage().delete(LOGIN_USER_KEY);

            // 2. 设置用户信息到ThreadLocal
            SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);

            // 3. 执行登录，生成Token（关键：基于设备类型隔离）
            StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());

            // 4. 设置用户信息到Token会话
            setLoginUser(loginUser);

            // 5. 验证Token隔离是否正确
            validateTokenIsolation(loginUser, deviceType);

            // 6. 记录登录成功日志
            logLoginSuccess(loginUser, deviceType);

        } catch (Exception e) {
            // 登录失败时清理可能的残留数据
            try {
                SaHolder.getStorage().delete(LOGIN_USER_KEY);
                if (StpUtil.isLogin()) {
                    StpUtil.logout();
                }
            } catch (Exception cleanupException) {
                // 忽略清理异常
            }
            throw new UtilException("登录失败: " + e.getMessage(), e);
        }
    }

    /**
     * 验证Token隔离是否正确
     */
    private static void validateTokenIsolation(LoginUser loginUser, DeviceType deviceType) {
        try {
            // 验证设备类型
            String actualDevice = StpUtil.getLoginDevice();
            if (!deviceType.getDevice().equals(actualDevice)) {
                throw new UtilException("Token设备类型隔离验证失败 - 期望: " + deviceType.getDevice() + ", 实际: " + actualDevice);
            }

            // 验证LoginId格式
            String actualLoginId = StpUtil.getLoginIdAsString();
            String expectedLoginId = loginUser.getLoginId();
            if (!expectedLoginId.equals(actualLoginId)) {
                throw new UtilException("Token LoginId验证失败 - 期望: " + expectedLoginId + ", 实际: " + actualLoginId);
            }

            // 验证Token唯一性
            String tokenValue = StpUtil.getTokenValue();
            if (tokenValue == null || tokenValue.length() < 32) {
                throw new UtilException("Token唯一性验证失败");
            }

        } catch (Exception e) {
            throw new UtilException("Token隔离验证失败: " + e.getMessage(), e);
        }
    }



    /**
     * 记录登录成功日志
     */
    private static void logLoginSuccess(LoginUser loginUser, DeviceType deviceType) {
        try {
            String tokenValue = StpUtil.getTokenValue();
            String tokenPrefix = tokenValue != null ? tokenValue.substring(0, Math.min(10, tokenValue.length())) + "..." : "null";

            System.out.println(String.format(
                "✅ 登录成功 - 用户类型: %s, 用户ID: %s, 设备类型: %s, Token前缀: %s, LoginId: %s, OpenId: %s",
                loginUser.getUserType(),
                loginUser.getUserId(),
                deviceType.getDevice(),
                tokenPrefix,
                loginUser.getLoginId(),
                loginUser.getOpenId() != null ? loginUser.getOpenId().substring(0, Math.min(10, loginUser.getOpenId().length())) + "..." : "null"
            ));
        } catch (Exception e) {
            // 忽略日志记录异常
        }
    }

    /**
     * 设置用户数据(多级缓存)
     */
    public static void setLoginUser(LoginUser loginUser) {
        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
    }

    /**
     * 获取用户(多级缓存)
     */
    public static LoginUser getLoginUser() {
        LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
        if (loginUser != null) {
            return loginUser;
        }
        loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
        return loginUser;
    }

    /**
     * 强制从Token获取用户信息，跳过ThreadLocal缓存
     * 用于防止线程池复用导致的用户信息串号
     */
    public static LoginUser getLoginUserFromToken() {
        // 直接从Redis Token缓存获取，跳过ThreadLocal
        LoginUser loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
        if (loginUser == null) {
            throw new UtilException("用户未登录或登录已过期");
        }

        // 验证Token和用户信息的一致性
        String expectedLoginId = loginUser.getLoginId();
        String actualLoginId = StpUtil.getLoginIdAsString();
        if (!expectedLoginId.equals(actualLoginId)) {
            throw new UtilException("用户身份验证失败，Token不匹配 - 期望: " + expectedLoginId + ", 实际: " + actualLoginId);
        }

        return loginUser;
    }

    /**
     * 获取设备
     */
    public static DeviceType getDeviceType() {
        String loginDevice = StpUtil.getLoginDevice();
        return DeviceType.getDeviceType(loginDevice);
    }

    /**
     * 获取用户id（安全版本）
     * 防止ThreadLocal污染导致的用户ID错误
     */
    public static Long getUserId() {
        // 优先尝试从安全方法获取
        try {
            LoginUser loginUser = getLoginUserFromToken();
            if (loginUser != null) {
                return loginUser.getUserId();
            }
        } catch (Exception e) {
            // 如果Token方法失败，使用备用方案
        }

        // 备用方案：直接从LoginId解析
        String loginId = StpUtil.getLoginIdAsString();
        String userId = null;
        for (UserType value : UserType.values()) {
            if (StringUtils.contains(loginId, value.getUserType())) {
                String[] strs = StringUtils.split(loginId, JOIN_CODE);
                // 用户id在总是在最后
                userId = strs[strs.length - 1];
                break;
            }
        }
        if (StringUtils.isBlank(userId)) {
            throw new UtilException("登录用户: LoginId异常 => " + loginId);
        }
        return Long.parseLong(userId);
    }

    /**
     * 获取部门ID（安全版本）
     */
    public static Long getDeptId() {
        try {
            return getLoginUserFromToken().getDeptId();
        } catch (Exception e) {
            return getLoginUser().getDeptId();
        }
    }

    /**
     * 获取用户账户（安全版本）
     */
    public static String getUsername() {
        try {
            return getLoginUserFromToken().getUsername();
        } catch (Exception e) {
            return getLoginUser().getUsername();
        }
    }

    /**
     * 获取用户类型
     */
    public static UserType getUserType() {
        String loginId = StpUtil.getLoginIdAsString();
        return UserType.getUserType(loginId);
    }

    /**
     * 是否为管理员
     *
     * @param userId 用户ID
     * @return 结果
     */
    public static boolean isAdmin(Long userId) {
        return UserConstants.ADMIN_ID.equals(userId);
    }

    public static boolean isAdmin() {
        return isAdmin(getUserId());
    }

}
